package com.itic.appbase.framework;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itic.appbase.framework.model.BaseParam;
import com.itic.appbase.framework.model.Bean;
import com.itic.appbase.framework.model.Case;
import com.itic.appbase.framework.model.Expect;
import com.itic.appbase.framework.model.Method;
import com.itic.appbase.framework.model.Param;
import com.itic.appbase.framework.model.TestParmar;
import com.itic.appbase.framework.util.AutoParam;
import com.itic.appbase.framework.util.TestConst;
import com.itic.appbase.framework.utils.StringHelper;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/base/appbase-config.xml",
                                    "/base/appbase-cxf.xml", 
                                    "/base/mvc-config.xml",
                                    "/base/mybatis-config.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
@ActiveProfiles("develop")
@WebAppConfiguration(value = "src/main/webapp")
public class BaseTestController extends
        AbstractTransactionalJUnit4SpringContextTests {
    protected MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setup() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();// 集成测试
        // RegionController regionController = new RegionController();
        // mockMvc = MockMvcBuilders.standaloneSetup(regionController).build();// 单元测试
    }
    
    public void runTest(String filePath) throws Exception{
        ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
        Bean bean = mapper.readValue(new File(filePath), Bean.class);
        List<Method>  methods = bean.getMethods();// 包含方法
        List<TestParmar> beans = bean.getBeans();// 包含实体
        List<BaseParam> baseParams = bean.getBaseParam();// 基本参数 
        for (Method method : methods) {
            runMethod(method,beans,baseParams);
        }
      }
      
      private void runMethod(Method method, List<TestParmar> beans, List<BaseParam> baseParams ) throws Exception{
         String methodType = method.getType();
         String methodUrl =  method.getUrl();
         Param methodParams =  method.getParams();
         String contentType = method.getContentType();
         List<Case> cases = method.getCases();
         cases = getParamLostCase(cases);
         // 参数 
         String[] methodValues =  methodParams.getValue().split(",");
         String[] methodRequired =  methodParams.getRequired().split(",");
         String lostField = "";// 要丢弃的必填字段

         for (Case case1 : cases) {
             System.out.println("-------------------------------测试案例: "+ case1.getName()+"---------------");
             if (case1.getName().equalsIgnoreCase("others")){
                 continue;
             }
             if (case1.getName().equalsIgnoreCase(TestConst.TEST_PARAM_LOST)){// 当测试必填参数丢失时
                 lostField = getRequiredParam(methodValues, methodRequired);
                 System.out.println("丢弃字段:" + lostField);
            }
             MockHttpServletRequestBuilder ms = null;
             if ("post".equalsIgnoreCase(methodType)){
                 ms = MockMvcRequestBuilders.post(methodUrl); 
             } else {
                 ms = MockMvcRequestBuilders.get(methodUrl); 
             }
             if ("json".equalsIgnoreCase(contentType)){
                 ms.contentType(MediaType.APPLICATION_JSON).accept("application/json");
             } else {
                 if ("html".equalsIgnoreCase(contentType)) {
                     ms.contentType(MediaType.TEXT_HTML).accept("text/html");
                 }
             }
            List<String> caseParamNames = new ArrayList<String>();// 特殊参数集合
            List<Param> caseParams = case1.getParams();
            // 特定参数
            if (null != caseParams && !caseParams.isEmpty()){
                for (Param param : caseParams) {
                    if (StringHelper.isNotBlank(lostField) && param.getKey().equalsIgnoreCase(lostField)){
                        continue;// 当此字段是要丢失的字段时
                    }
                    caseParamNames.add(param.getKey());// 特殊参数集合
                    BaseParam baseParam = getParamByKey(param.getKey(),baseParams);// 根据key获得参数对象
                    if (StringHelper.isNotBlank(param.getValue())){// 为请求链接添加请求参数
                        ms.param(baseParam.getName(), param.getValue());
                    } else if(StringHelper.isNotBlank(param.getValues())) {
                        ms.param(baseParam.getName(), AutoParam.auto(param.getValues().split(",")).toString());
                    }
                }
            }
           
            
            
            for (int i = 0; i < methodValues.length; i ++) {
                
                if (!caseParamNames.contains(methodValues[i])){// 当上面的特定参数中不包括此参数时，才追加此参数
                    if (StringHelper.isNotBlank(lostField) && methodValues[i].equalsIgnoreCase(lostField)){
                        continue;// 当此字段是要丢失的字段时
                    }
                    BaseParam baseParam = getParamByKey(methodValues[i],baseParams);
                    Class<?> clazz = switchClass(baseParam.getType());// 此参数的类型
                    if ("true".equalsIgnoreCase(methodRequired[i]) || (Boolean)AutoParam.auto(boolean.class)){ // 必填参数 或者 随机赋值于非必填
                        String value = null;
                        if (null == baseParam.getScope()){// 参数值不是范围类型
                            if (null != baseParam.getBean()){
                                value =  String.valueOf( AutoParam.auto(getBeanByName(baseParam.getBean(),beans).getValue().split(",")));
                            } else {
                                value = baseParam.getValue();
                            }
                        } else {// 参数值是范围类型
                            if (null != baseParam.getScope().getLength()){
                                value = String.valueOf(clazz.cast(AutoParam.auto(clazz, Integer.valueOf(baseParam.getScope().getLength()))));
                            } else {
                                if (StringHelper.isNotBlank(baseParam.getScope().getStyle())){
                                    if (Float.class.equals(clazz)){
                                        value = (String)AutoParam.auto(clazz, Float.valueOf(baseParam.getScope().getMin()), Float.valueOf(baseParam.getScope().getMax()), baseParam.getScope().getStyle());
                                    }
                                    if (Double.class.equals(clazz)){
                                        value = (String)AutoParam.auto(clazz, Double.valueOf(baseParam.getScope().getMin()), Double.valueOf(baseParam.getScope().getMax()), baseParam.getScope().getStyle());
                                    }
                                    if (Long.class.equals(clazz)){
                                        value = (String)AutoParam.auto(clazz, Long.valueOf(baseParam.getScope().getMin()), Long.valueOf(baseParam.getScope().getMax()), baseParam.getScope().getStyle());
                                    }
                                    if (Date.class.equals(clazz)){
                                        value = String.valueOf(clazz.cast(AutoParam.auto(clazz, baseParam.getScope().getMin(), baseParam.getScope().getMax(), baseParam.getScope().getStyle())));
                                    }
                                } else {
                                    if (Date.class.equals(clazz)){
                                        value = String.valueOf(clazz.cast(AutoParam.auto(clazz, baseParam.getScope().getMin(), baseParam.getScope().getMax())));
                                    } else {
                                        value = String.valueOf(clazz.cast(AutoParam.auto(clazz, Integer.valueOf(baseParam.getScope().getMin()), Integer.valueOf(baseParam.getScope().getMax()))));
                                    }
                                } 
                            } 
                        }
                        ms.param(baseParam.getName(), value);
                    } 
                }
            }
            
            // 期望
            List<Expect> expects = case1.getExpect();
            ResultActions ra = mockMvc.perform(ms);
            if( null != expects){
                for (Expect expect : expects) {
                    if ( "json".equalsIgnoreCase(expect.getType())){
                        ra.andExpect(jsonPath("$."+ expect.getName() ).value(expect.getValue()));
                    } else {
                        if ( "attribute".equalsIgnoreCase(expect.getType())){
                            if (StringHelper.isBlank(expect.getValue())){
                                ra.andExpect(MockMvcResultMatchers.model().attributeExists(expect.getName())); 
                            }
                        }
                        if ( "page".equalsIgnoreCase(expect.getType())){
                            ra.andExpect(MockMvcResultMatchers.view().name(expect.getValue()));
                        }
                    }
                }
            }
           //ra.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
           ra.andDo(MockMvcResultHandlers.print()).andReturn();
         }
      }
      
       private List<Case> getParamLostCase(List<Case> cases){
           boolean isFound = false;
           for (Case case1 : cases) {
             if(case1.getName().equalsIgnoreCase(TestConst.TEST_PARAM_LOST)){
                 isFound = true;
                 break;
             }
           }
           if (!isFound){
               Case c = new Case();
               c.setName(TestConst.TEST_PARAM_LOST);
               cases.add(c);
           }
           return cases;
       }
      
       // 从必填字段中随便找一个，并返回此字段的名字
       private String getRequiredParam(String[] methodValues, String[] methodRequired){
           List<String> requiredFields = new ArrayList<String>();
           for (int i = 0; i < methodRequired.length; i++) {
              if ("true".equalsIgnoreCase(methodRequired[i])){
                  requiredFields.add(methodValues[i]);
              }
           }
           int a = (Integer)AutoParam.auto(Integer.class, 0, requiredFields.size());
           return requiredFields.get(a);
       } 
      
       private BaseParam getParamByKey(String key, List<BaseParam> params) {
           BaseParam bp = null;
           for (BaseParam baseParam : params) {
               if (key.equalsIgnoreCase(baseParam.getKey())) {
                   bp = baseParam;
                   break;
               }
           }
           return bp;
       }
       
       private TestParmar getBeanByName(String name, List<TestParmar> beas){
           TestParmar b = null;
           for (TestParmar bean : beas) {
               if(bean.getName().equalsIgnoreCase(name)){
                   b = bean;
                   break;
               }
           }
           return b;
       }
      
       private Class<?> switchClass(String clazz) throws ClassNotFoundException{
          return  Class.forName(clazz);
       } 
}
